<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>一维数据自然分段可视化分析</title>
    <!-- 引入Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- 引入Font Awesome -->
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <!-- 引入ECharts -->
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
</head>
<body class="bg-gray-50 min-h-screen">
    <div class="container mx-auto px-4 py-8 max-w-7xl">
        <header class="mb-8">
            <h1 class="text-[clamp(1.8rem,4vw,2.5rem)] font-bold text-gray-800 mb-2">
                <i class="fa fa-bar-chart text-indigo-600 mr-2"></i>一维数据自然分段分析
            </h1>
            <p class="text-gray-600 max-w-3xl">
                该可视化展示了一维数据基于自然分段算法的分析结果，包括数据分布、分段边界及各分段的统计特征
            </p>
        </header>

        <main class="space-y-8">
            <!-- 控制区域 -->
            <div class="bg-white rounded-xl shadow-md p-4 md:p-6 transition-all duration-300">
                <div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
                    <div>
                        <h2 class="text-xl font-semibold text-gray-700 mb-1">数据控制</h2>
                        <p class="text-sm text-gray-500">调整参数以重新生成或加载数据</p>
                    </div>
                    <div class="flex flex-wrap gap-3">
                        <button id="generateData" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-md text-sm transition-all flex items-center">
                            <i class="fa fa-random mr-2"></i>生成随机数据
                        </button>
                        <button id="refreshChart" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md text-sm transition-all flex items-center">
                            <i class="fa fa-refresh mr-2"></i>刷新图表
                        </button>
                        <button id="downloadData" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-md text-sm transition-all flex items-center">
                            <i class="fa fa-download mr-2"></i>下载数据
                        </button>
                    </div>
                </div>
                
                <div class="mt-4 grid grid-cols-1 md:grid-cols-3 gap-4">
                    <div>
                        <label class="block text-sm font-medium text-gray-700 mb-1">数据点数量</label>
                        <input type="range" id="dataCount" min="50" max="500" value="200" 
                            class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-indigo-600">
                        <div class="flex justify-between text-xs text-gray-500 mt-1">
                            <span>50</span>
                            <span id="dataCountValue">200</span>
                            <span>500</span>
                        </div>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 mb-1">预计分段数</label>
                        <input type="range" id="segmentCount" min="2" max="10" value="5" 
                            class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-indigo-600">
                        <div class="flex justify-between text-xs text-gray-500 mt-1">
                            <span>2</span>
                            <span id="segmentCountValue">5</span>
                            <span>10</span>
                        </div>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-gray-700 mb-1">数据范围</label>
                        <div class="flex gap-2">
                            <input type="number" id="minValue" value="0" min="0" max="1000"
                                class="flex-1 border border-gray-300 rounded-md px-2 py-1 text-sm">
                            <span class="flex items-center text-gray-500">-</span>
                            <input type="number" id="maxValue" value="1000" min="0" max="2000"
                                class="flex-1 border border-gray-300 rounded-md px-2 py-1 text-sm">
                        </div>
                    </div>
                </div>
            </div>

            <!-- 主要可视化区域 -->
            <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
                <!-- 散点分布图 -->
                <div class="lg:col-span-3 bg-white rounded-xl shadow-md p-4 md:p-6 transition-all duration-300 hover:shadow-lg">
                    <h2 class="text-xl font-semibold text-gray-700 mb-4">数据分布与自然分段</h2>
                    <div id="scatterChart" class="w-full h-[400px] md:h-[500px] rounded-lg border border-gray-100"></div>
                </div>

                <!-- 分段统计 -->
                <div class="bg-white rounded-xl shadow-md p-4 md:p-6 transition-all duration-300 hover:shadow-lg">
                    <h2 class="text-xl font-semibold text-gray-700 mb-4">分段数据统计</h2>
                    <div id="segmentStatsChart" class="w-full h-[300px] md:h-[350px] rounded-lg border border-gray-100"></div>
                </div>

                <!-- 分段详情 -->
                <div class="lg:col-span-2 bg-white rounded-xl shadow-md p-4 md:p-6 transition-all duration-300 hover:shadow-lg">
                    <h2 class="text-xl font-semibold text-gray-700 mb-4">分段详情</h2>
                    <div class="overflow-x-auto">
                        <table class="min-w-full divide-y divide-gray-200">
                            <thead>
                                <tr>
                                    <th class="px-4 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider rounded-tl-lg">分段</th>
                                    <th class="px-4 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">范围</th>
                                    <th class="px-4 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">数据点数量</th>
                                    <th class="px-4 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">平均值</th>
                                    <th class="px-4 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider rounded-tr-lg">标准差</th>
                                </tr>
                            </thead>
                            <tbody id="segmentDetails" class="bg-white divide-y divide-gray-200">
                                <!-- 动态填充内容 -->
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>

            <!-- 分析说明 -->
            <div class="bg-white rounded-xl shadow-md p-6 text-gray-700">
                <h3 class="text-lg font-semibold mb-3 flex items-center">
                    <i class="fa fa-info-circle text-indigo-500 mr-2"></i>自然分段算法说明
                </h3>
                <p class="mb-3">自然分段算法基于数据的分布特征，自动识别数据中的自然分组。算法通过分析数据密度和分布连续性，找到最优的分割点，使每个分段内的数据具有较高的相似性，而不同分段间的数据具有明显差异。</p>
                <p>本可视化工具可以帮助您：</p>
                <ul class="list-disc pl-5 space-y-1 mt-2">
                    <li>直观了解数据的分布特征和自然分组</li>
                    <li>分析各分段的数据量和统计特征</li>
                    <li>通过调整参数探索不同的分段可能性</li>
                    <li>导出分析结果用于进一步研究</li>
                </ul>
            </div>
        </main>

        <footer class="mt-12 text-center text-gray-500 text-sm pb-8">
            <p>一维数据自然分段可视化分析工具 &copy; 2023</p>
        </footer>
    </div>

    <script>
        // 初始化图表
        const scatterChartDom = document.getElementById('scatterChart');
        const statsChartDom = document.getElementById('segmentStatsChart');
        const scatterChart = echarts.init(scatterChartDom);
        const statsChart = echarts.init(statsChartDom);
        
        // 全局数据存储
        let data = {
            points: [],       // 原始数据点
            segments: [],     // 分段信息
            segmentPoints: [] // 按分段组织的数据点
        };

        // 更新滑块显示值
        document.getElementById('dataCount').addEventListener('input', function() {
            document.getElementById('dataCountValue').textContent = this.value;
        });
        
        document.getElementById('segmentCount').addEventListener('input', function() {
            document.getElementById('segmentCountValue').textContent = this.value;
        });

        // 生成符合自然分段特征的随机数据
        function generateNaturalSegmentData() {
            const count = parseInt(document.getElementById('dataCount').value);
            const targetSegments = parseInt(document.getElementById('segmentCount').value);
            const minVal = parseFloat(document.getElementById('minValue').value);
            const maxVal = parseFloat(document.getElementById('maxValue').value);
            
            // 清空现有数据
            data.points = [];
            data.segments = [];
            data.segmentPoints = [];
            
            // 计算段间距
            const range = maxVal - minVal;
            const segmentSize = range / (targetSegments + (targetSegments - 1) * 0.5); // 包含间隙
            
            // 生成每个分段的中心和标准差
            const segmentCenters = [];
            let currentPos = minVal + segmentSize / 2;
            
            for (let i = 0; i < targetSegments; i++) {
                segmentCenters.push(currentPos);
                currentPos += segmentSize * 1.5; // 分段间留出间隙
            }
            
            // 为每个分段生成数据点，总数量接近count
            const pointsPerSegment = Math.floor(count / targetSegments);
            const colors = ['#36a2eb', '#ff6384', '#ffce56', '#4bc0c0', '#9966ff', '#c9cbcf', '#ff9f40', '#6dd230', '#ea3323', '#34a853'];
            
            // 计算实际分段边界（基于数据分布）
            const boundaries = [];
            for (let i = 0; i < targetSegments - 1; i++) {
                boundaries.push((segmentCenters[i] + segmentCenters[i + 1]) / 2);
            }
            
            // 生成数据点
            for (let i = 0; i < targetSegments; i++) {
                const pointsInSegment = i === targetSegments - 1 
                    ? count - i * pointsPerSegment 
                    : pointsPerSegment;
                
                const segmentPoints = [];
                const stdDev = segmentSize * 0.2; // 标准差，控制数据分散程度
                
                for (let j = 0; j < pointsInSegment; j++) {
                    // 使用正态分布生成更自然的数据分布
                    const value = segmentCenters[i] + normalRandom() * stdDev;
                    data.points.push({value, segment: i});
                    segmentPoints.push(value);
                }
                
                // 记录分段信息
                const min = Math.min(...segmentPoints);
                const max = Math.max(...segmentPoints);
                const avg = segmentPoints.reduce((sum, val) => sum + val, 0) / segmentPoints.length;
                const variance = segmentPoints.reduce((sum, val) => sum + Math.pow(val - avg, 2), 0) / segmentPoints.length;
                const std = Math.sqrt(variance);
                
                data.segments.push({
                    id: i,
                    name: `分段 ${i + 1}`,
                    color: colors[i % colors.length],
                    min: parseFloat(min.toFixed(2)),
                    max: parseFloat(max.toFixed(2)),
                    count: segmentPoints.length,
                    average: parseFloat(avg.toFixed(2)),
                    std: parseFloat(std.toFixed(2))
                });
                
                data.segmentPoints.push(segmentPoints.map(val => [val, Math.random()])); // y值随机，便于散点图展示
            }
            
            return data;
        }

        // 生成正态分布随机数（Box-Muller算法）
        function normalRandom() {
            let u = 0, v = 0;
            while (u === 0) u = Math.random(); // 避免log(0)
            while (v === 0) v = Math.random();
            return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
        }

        // 更新散点图
        function updateScatterChart() {
            // 准备数据
            const series = data.segments.map((segment, index) => ({
                name: segment.name,
                type: 'scatter',
                data: data.segmentPoints[index],
                symbolSize: 8,
                itemStyle: {
                    color: segment.color,
                    opacity: 0.7,
                    shadowBlur: 5
                },
                emphasis: {
                    itemStyle: {
                        opacity: 1,
                        shadowBlur: 10,
                        symbolSize: 12
                    }
                },
                tooltip: {
                    formatter: function(params) {
                        return `${segment.name}<br/>值: ${params.data[0].toFixed(2)}`;
                    }
                }
            }));
            
            // 添加分段边界线
            const boundaryLines = data.segments.slice(0, -1).map((segment, index) => {
                const boundary = (segment.max + data.segments[index + 1].min) / 2;
                return {
                    name: `分段边界 ${index + 1}-${index + 2}`,
                    type: 'line',
                    data: [[boundary, 0], [boundary, 1]],
                    lineStyle: {
                        color: '#999',
                        width: 2,
                        type: 'dashed'
                    },
                    symbol: 'none',
                    tooltip: {
                        formatter: `边界值: ${boundary.toFixed(2)}`
                    },
                    z: 100 // 确保边界线在最上层
                };
            });
            
            // 合并所有系列
            const allSeries = [...series, ...boundaryLines];
            
            // 配置图表
            const option = {
                tooltip: {
                    trigger: 'item'
                },
                legend: {
                    data: data.segments.map(s => s.name),
                    top: 10,
                    textStyle: {
                        color: '#666'
                    },
                    pageIconSize: 12,
                    pageTextStyle: {
                        fontSize: 12
                    }
                },
                grid: {
                    left: '5%',
                    right: '5%',
                    bottom: '10%',
                    top: '15%',
                    containLabel: true
                },
                xAxis: {
                    type: 'value',
                    name: '数据值',
                    nameLocation: 'middle',
                    nameGap: 30,
                    axisLine: {
                        lineStyle: {
                            color: '#ddd'
                        }
                    },
                    splitLine: {
                        lineStyle: {
                            color: '#f0f0f0'
                        }
                    }
                },
                yAxis: {
                    type: 'value',
                    show: false, // 隐藏Y轴，因为我们只关心X值
                    min: 0,
                    max: 1
                },
                series: allSeries
            };
            
            scatterChart.setOption(option);
        }

        // 更新统计图表
        function updateStatsChart() {
            const option = {
                tooltip: {
                    trigger: 'axis',
                    axisPointer: {
                        type: 'shadow'
                    },
                    formatter: function(params) {
                        const segment = data.segments.find(s => s.name === params[0].name);
                        return `${segment.name}<br/>数据点数量: ${segment.count}`;
                    }
                },
                grid: {
                    left: '3%',
                    right: '4%',
                    bottom: '3%',
                    containLabel: true
                },
                xAxis: {
                    type: 'category',
                    data: data.segments.map(s => s.name),
                    axisLine: {
                        lineStyle: {
                            color: '#ddd'
                        }
                    }
                },
                yAxis: {
                    type: 'value',
                    name: '数据点数量',
                    axisLine: {
                        lineStyle: {
                            color: '#ddd'
                        }
                    },
                    splitLine: {
                        lineStyle: {
                            color: '#f0f0f0'
                        }
                    }
                },
                series: [
                    {
                        name: '数据点数量',
                        type: 'bar',
                        data: data.segments.map(s => s.count),
                        itemStyle: {
                            color: function(params) {
                                return data.segments[params.dataIndex].color;
                            }
                        },
                        emphasis: {
                            itemStyle: {
                                opacity: 0.8
                            }
                        }
                    }
                ]
            };
            
            statsChart.setOption(option);
        }

        // 更新分段详情表格
        function updateSegmentDetails() {
            const tableBody = document.getElementById('segmentDetails');
            tableBody.innerHTML = '';
            
            data.segments.forEach(segment => {
                const row = document.createElement('tr');
                row.className = 'hover:bg-gray-50 transition-colors';
                row.innerHTML = `
                    <td class="px-4 py-3 whitespace-nowrap">
                        <div class="flex items-center">
                            <div class="w-3 h-3 rounded-full mr-2" style="background-color: ${segment.color}"></div>
                            <span class="text-sm font-medium text-gray-900">${segment.name}</span>
                        </div>
                    </td>
                    <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${segment.min} - ${segment.max}</td>
                    <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${segment.count}</td>
                    <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${segment.average}</td>
                    <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${segment.std}</td>
                `;
                tableBody.appendChild(row);
            });
        }

        // 刷新所有图表
        function refreshAllCharts() {
            // 显示加载状态
            document.getElementById('refreshChart').innerHTML = '<i class="fa fa-spinner fa-spin mr-2"></i>刷新中...';
            document.getElementById('refreshChart').disabled = true;
            
            setTimeout(() => {
                generateNaturalSegmentData();
                updateScatterChart();
                updateStatsChart();
                updateSegmentDetails();
                
                // 恢复按钮状态
                document.getElementById('refreshChart').innerHTML = '<i class="fa fa-refresh mr-2"></i>刷新图表';
                document.getElementById('refreshChart').disabled = false;
            }, 600);
        }

        // 下载数据
        function downloadData() {
            // 准备CSV数据
            let csvContent = "值,分段\n";
            data.points.forEach(point => {
                csvContent += `${point.value.toFixed(2)},${data.segments[point.segment].name}\n`;
            });
            
            // 添加分段信息
            csvContent += "\n分段,最小值,最大值,数据点数量,平均值,标准差\n";
            data.segments.forEach(segment => {
                csvContent += `${segment.name},${segment.min},${segment.max},${segment.count},${segment.average},${segment.std}\n`;
            });
            
            // 创建下载链接
            const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
            const link = document.createElement('a');
            const url = URL.createObjectURL(blob);
            
            link.setAttribute('href', url);
            link.setAttribute('download', '数据自然分段分析结果.csv');
            link.style.visibility = 'hidden';
            
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }

        // 绑定事件
        document.getElementById('generateData').addEventListener('click', refreshAllCharts);
        document.getElementById('refreshChart').addEventListener('click', refreshAllCharts);
        document.getElementById('downloadData').addEventListener('click', downloadData);

        // 响应窗口大小变化
        window.addEventListener('resize', function() {
            scatterChart.resize();
            statsChart.resize();
        });

        // 初始加载
        window.addEventListener('load', function() {
            refreshAllCharts();
        });
    </script>
</body>
</html>
